﻿using System;
using System.Data;
using System.Configuration;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.ComponentModel;
using System.Xml.Serialization;
using Microsoft.SharePoint.WebPartPages;

using GoodStuff.Text;
using Microsoft.SharePoint;

namespace GoodStuff.SharePoint2007
{
    /// <summary>
    /// GoodStuff BaseWebpart which handles the loading of a WebPart that it's wrapped around an usercontrol
    /// </summary>
    public abstract class BaseWebPart : Microsoft.SharePoint.WebPartPages.WebPart
    {
        /// <summary>
        /// The property where the custom display name of the WebPart is stored in.
        /// Override this property to apply your own attributes (name, description etc) 
        /// </summary>
        [Category("Eigenschappen")]
        [WebDisplayName("Titel van dit WebPart")]
        [WebDescription("Titel van dit WebPart voor de eindgebruikers.")]
        [WebBrowsable(false)]
        [Personalizable(PersonalizationScope.Shared)]
        // The accessor for this property.
        public virtual string CustomDisplayTitle {get;set;}

        /// <summary>
        /// The serverrelative path where all usercontrols are located. The 'basepath' will be combined with the name of the filename of the usercontrol you specify in childclasses.
        /// It's advised to override this property in a basewebpart-class of your project. Specify with / at the end.  Example: @"~/_controltemplates/AkzoNobel/FourSecrets/".
        /// </summary>
        protected abstract string UserControlPath { get; }

        /// <summary>
        /// The filename of the usercontrol that should be loaded by this webpart. This filename will be combined with the UserControlPath that is specified also (advise: in basewebpart).
        /// Example: @"ReportControl.ascx"; Default the typename of the usercontrol will be used with the ".ascx"-suffix. Override it to create your own name of pattern.
        /// </summary>
        protected abstract string UserControlFileName { get; }

        /// <summary>
        /// Some webparts should not be rendered while in edit mode. Override this propery to return true when you don't want to
        /// render this webpart in edit mode. show a message 'Dit webonderdeel wordt niet getoond in beheermode' in stead of rendering the webpart
        /// </summary>
        protected virtual bool HideInEditMode
        {
            get
            {
                return false;
            }
        }

        /// <summary>
        /// Default ChromeType is 'none'. Override to specify something else.
        /// </summary>
        public override PartChromeType ChromeType
        {
            get
            {
                return PartChromeType.None;
            }
            set
            {
                base.ChromeType = value;
            }
        }

        /// <summary>
        /// Override of the default DisplayTitle. When HideDefaultProperties is true, the user will not be able to set the displaytitle
        /// of the WebPart. In that case, the 'CustomDisplayTitle' is used.
        /// </summary>
        public new string DisplayTitle
        {
            get
            {
                if (this.HideDefaultProperties && !string.IsNullOrEmpty(this.CustomDisplayTitle))
                {
                    return this.CustomDisplayTitle;
                }
                else
                {
                    return base.DisplayTitle;
                }
            }
        }

        /// <summary>
        /// Hide the default properties when editing a WebPart?
        /// </summary>
        public virtual bool HideDefaultProperties
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// When HideDefaultProperties is set to true, all custom properties will be loaded within a custom Toolpane. To expand
        /// a categorie (of properties) by default, you can specify the name of that category by overriding this property.
        /// </summary>
        public virtual string ToolPaneExpandCategory
        {
            get
            {
                return "Eigenschappen";
            }
        }

        /// <summary>
        /// Default message when a WebPart isn't rendered because it should be hidden in edit mode. 
        /// Specify as formatstring with only one parameter -> {0}. Will be replaced with the name of the webpart
        /// </summary>
        protected virtual string MessageHiddenWebPartInEditModeFormatString
        {
            get
            {
                return "Het webonderdeel {0} wordt niet geladen in beheermodus. Verlaat de bewerkmodus om het webonderdeel te kunnen bekijken.";
            }
        }

        /// <summary>
        /// Default message when a WebPart isn't loaded succesfully. Target = enduser. Override to specify a custom message.
        /// </summary>
        protected virtual string MessageErrorLoadingWebPart
        {
            get
            {
                return "Er is een fout opgetreden voor dit webonderdeel. Onze excuses voor het ongemak";
            }
        }

        /// <summary>
        /// Default message when an error occurs within a WebPart. Target = authors/editors. Override to specify a custom message.
        /// </summary>
        protected virtual string MessageErrorInWebPart
        {
            get
            {
                return "Fout opgetreden in WebPart";
            }
        }

        /// <summary>
        /// Is the webpart currently loaded within edit mode?
        /// </summary>
        /// <remarks>http://blogit.create.pt/blogs/andrevala/archive/2007/06/22/WSS-Tip-_2300_15_3A00_-Testar-o-Display-Mode-de-uma-Web-Part.aspx</remarks>
        protected virtual bool EditMode
        {
            get
            {
                return this.WebPartManager != null && this.WebPartManager.DisplayMode != WebPartManager.BrowseDisplayMode;
            }
        }

        /// <summary>
        /// Check's if this webpart should be hidden in edit mode. If so, a message will be presented on the page. You can change this message
        /// be overriding the property MessageHiddenWebPartInEditModeFormatString
        /// </summary>
        /// <returns></returns>
        protected bool RenderingCancelledInEditMode()
        {
            if (this.HideInEditMode == true && this.EditMode == true)
            {
                this.Controls.Add(new LiteralControl(string.Format(this.MessageHiddenWebPartInEditModeFormatString, this.DisplayTitle)));
                return true;
            }
            return false;
        }

        /// <summary>
        /// Method which checks if technical details can be presented on the page. These details should only be presented when
        /// the user has permissions to edit the current page (=listitem).
        /// </summary>
        /// <remarks>This method relies on the current item of the current SPContext, so when it's not available, false will be returned</remarks>
        protected bool CanShowErrorDetails
        {
            get
            {
                //This method relies on the current item of the current SPContext, so when it's not available, we will return false
                try
                {
                    if (SPContext.Current == null)
                        return false;
                }
                catch (NullReferenceException)
                {
                    return false;
                }

                //Check if the user has editpermission for the current listitem. Note: WebPart can also be
                //present on a basic (non publishing) WebPart-page. In that case, we're dealing with a SPItem
                //in stead of a SPListItem

                if (SPContext.Current.Item is SPListItem)
                {
                    return SPContext.Current.ListItem.DoesUserHavePermissions(SPBasePermissions.EditListItems);
                }
                else
                {
                    //Only show details when user can design the page with WebParts
                    return this.WebPartManager != null && this.WebPartManager.DisplayMode == WebPartManager.DesignDisplayMode;
                }
            }
        }

        /// <summary>
        /// Get all controls present within this WebPart. Overriden from baseclass
        /// </summary>        
        public override ControlCollection Controls
        {
            get
            {
                EnsureChildControls();
                return base.Controls;
            }
        }

        /// <summary>
        /// Can be overriden to attach validation afther usercontrol is loaded/initiated, but behore usercontrol will be added to the controlcollection (page / webpart).
        /// You can parse the basecontrol to your specific usercontrol to check properties are configured or other validation you want to do. 
        /// Throw a specific Exception when you want to stop processing the loading of the webpart/usercontrol
        /// </summary>
        /// <param name="control"></param>
        /// <remarks></remarks>
        protected virtual void ControlValidation(Control control)
        {
        }

        /// <summary>
        /// Can be overriden to perform additional actions (setting properties) when the usercontrol has been loaded by the WebPart.
        /// </summary>
        /// <param name="control"></param>
        protected virtual void ControlLoaded(Control control)
        {
        }

        /// <summary>
        /// The method where it all starts
        /// </summary>
        protected override void CreateChildControls()
        {
            //Check if we should cancel the loading of this webpart/control
            if (this.RenderingCancelledInEditMode())
                return; //cancel further loading of this webpart. Message already has been added to the controlcollection.

            //Check if control-basepath has been overriden/implemented, without it we can't continue
            if (string.IsNullOrEmpty(this.UserControlPath))
                throw new NotSupportedException("The abstract method 'UserControlPath' isn't overriden in the (base)webpart that is loaded.");

            //Override the default chrometype
            base.ChromeType = this.ChromeType;

            //Set the displayTitle in our custom display title when it's still empty
            //Compare with DisplayTile from BASE!
            if (string.IsNullOrEmpty(this.CustomDisplayTitle) && !string.IsNullOrEmpty(base.DisplayTitle))
            {
                this.CustomDisplayTitle = base.DisplayTitle;
            }

            try
            {             
                //Ensure trailing slash
                string baseControlPath = this.UserControlPath.EnsureEndWithSlash();

                Control childControl = (Control)Page.LoadControl(baseControlPath + UserControlFileName);
                //TODO check for right usercontrol(type)
                BaseUserControl baseUserControl = childControl as BaseUserControl;
                if (baseUserControl != null)
                {
                    //We can use the DisplayTitle of THIS class, which will check what is the right title to use                    
                    baseUserControl.DisplayTitle = this.DisplayTitle; //Caution! "new" implementation of this property
                }

                //Valide control (when implemented/override in child class)
                this.ControlValidation(childControl);

                //Add loaded control to the controlcollection
                this.Controls.Add(childControl);

                //Optional post-load-actions (when implemented/override in child class)
                this.ControlLoaded(childControl);
            }
            catch (Exception ex)
            {
                this.HandleException(ex);
            }
            finally
            {
                base.CreateChildControls();
            }
        }

        /// <summary>
        /// Overrides the default toolparts when HideDefaultProperties is set to true.
        /// </summary>
        /// <returns></returns>
        public override ToolPart[] GetToolParts()
        {
            if (this.HideDefaultProperties)
            {
                ToolPart[] toolparts = new ToolPart[1];

                CustomPropertyToolPart cptp = new CustomPropertyToolPart();
                if(!string.IsNullOrEmpty(this.ToolPaneExpandCategory))
                {
                    cptp.Expand(this.ToolPaneExpandCategory);
                }
                toolparts[0] = cptp;

                return toolparts;
            }
            else
            {
                return base.GetToolParts();
            }
        }

        /// <summary>
        /// When a parameter with the value of this property exists in the querystring, in case of an error the error will be thrown.
        /// Otherwise the error is added as text/literal within the webpart. Defaultname = 'dump' (?dump=true), override it to configure
        /// your own parametername
        /// </summary>
        /// <returns></returns>
        protected virtual string ThrowExceptionQuerystringParameter
        {
            get
            {
                return "dump";
            }
        }

        /// <summary>
        /// Function that handles the Exception. Will check which kind of (technical) information can be loaded to the screen.
        /// The method LogException will be called afterwards. Override this method if you want to log the Exception.
        /// Note: when you specify throw=true in the querystring, 
        /// </summary>
        /// <param name="ex"></param>
        protected void HandleException(Exception ex)
        {
            //Can we show full details?
            if (this.CanShowErrorDetails)
            {
                //If a parameter for querystring has been specified, and it's present within the querystring, the Exception
                //will be thrown in stead of presented on the page.
                if (!string.IsNullOrEmpty(this.ThrowExceptionQuerystringParameter)
                    && this.Page.Request.QueryString[this.ThrowExceptionQuerystringParameter] != null)
                {
                    throw ex;
                }
                else
                {
                    //Show message with full details.
                    this.Controls.Add(new LiteralControl(string.Format("{0} {1}: {2}", this.MessageErrorInWebPart, this.DisplayTitle, ex)));
                }
            }
            else
            {
                //Show friendly message without technical details.
                this.Controls.Add(new LiteralControl(this.MessageErrorLoadingWebPart));
                this.Page.Trace.Warn(this.GetType().FullName, ex.Message, ex);
            }

            this.LogException(ex);
        }

        protected virtual void LogException(Exception ex)
        {
            
        }
    }
}
